/*
 * Copyright (C) Francisco Morero Peyrona. All rights reserved.
 *
 * This software is published under the terms of Open Source
 * License version 1.1, a copy of which has been included with this
 * distribution in the License.txt file.
 */

package jdbcmanager.table;

import java.sql.*;
import java.awt.*;
import java.awt.event.*;

import javax.swing.*;
import javax.swing.event.*;

import jdbcmanager.app.*;
import jdbcmanager.swing.*;
import jdbcmanager.swing.text.*;

/**
 * Executes SQL conmmands and show ResultSet (if any) in a grid. 
 *
 * @author Francisco Morero Peyrona
 */

public final class DbTable extends JPanel implements ActionListener
{   
   // Extended actions
   private ExecAction execAction = new ExecAction(); 

   // GUI Controls
   private Editor    editor;
   private History   history;
   private DataGrid  grid;
   private StatusBar statusBar;
   
   //-------------------------------------------------------------------------//

   /**
    * Constructor
    *
    * @param conn
    */
   public DbTable( Connection conn )
   {
      init( conn );
   }

   /**
    * Shows passed commnad in SQL editor and executes it, showing 
    * retrieved data (if any) in the grid.
    * 
    * @param sCommand SQL staetement to be executed aginst a DBMS
    */
   public void executeSqlCommand( String sCommand )
   {
      this.editor.setText( sCommand );
      execute();
   }
   
   /**
    * Get SQL command at the SQL editor.
    * 
    * @return Get SQL command at the SQL editor
    */
   public String getSQLCommand()
   {
      return this.editor.getText();
   }

   /**
    * This method is invoked by every button in the toolbar
    * 
    * @param evt
    */
   public void actionPerformed( ActionEvent evt )
   {
      String sCommand = evt.getActionCommand();

      // Actions from Status Tool Bar
      if(      sCommand.equals( "FIRST"         ) )  this.grid.first();
      else if( sCommand.equals( "PREVIOUS"      ) )  this.grid.previous();
      else if( sCommand.equals( "NEXT"          ) )  this.grid.next();
      else if( sCommand.equals( "LAST"          ) )  this.grid.last();
      else if( sCommand.equals( "NEW"           ) )  this.grid.addRow();
      else if( sCommand.equals( "DELETE"        ) )  this.grid.delRow();
      else if( sCommand.equals( "ZOOM_IN"       ) )  this.grid.setFontSize(  1 );
      else if( sCommand.equals( "ZOOM_OUT"      ) )  this.grid.setFontSize( -1 );
      else if( sCommand.equals( "META_INFO"     ) )  doMetaDataDlg();
      else if( sCommand.equals( "UPDATABLE"     ) )  toogleUpdatable();
      else if( sCommand.equals( "PRINT"         ) )  printTable();
      else if( sCommand.equals( "EXPORT"		) )  exportTable();
   }
   
   //-------------------------------------------------------------------------//
   
   // Executes SQL command written in the SQL editor
   private void execute()
   {
      boolean bUpdatable = App.props().getSQLGridUnlocked( false );
      String  sCommand   = this.editor.getText();
      
      if( sCommand.length() > 0 )
      {      
          this.grid.execute( sCommand, bUpdatable, this.editor.getMaxRows() );
          this.statusBar.setUpdatableRowtSet( this.grid.isUpdateable(), this.grid.canBeUpdateable() );
          this.history.addSQL( sCommand );
      }
      else
      {
          App.showMessage( "Can't execute", "Can't execute: command is empty." );
      }
   }
   
   // Shows Resultset Metadata information in a dialog
   private void doMetaDataDlg()
   {
      if( this.grid != null )
      {
         ResultSetInfo dlg = new ResultSetInfo( this.grid.getMetaData() );
                       dlg.pack();
                       dlg.center();
                       dlg.setVisible( true );
      }
      else
      {
          App.showMessage( "Can't show ResultSet MetaData: there is no ResultSet" );
      }
   }
   
   private void toogleUpdatable()
   {
      this.grid.setUpdateable( ! this.grid.isUpdateable() );
      this.statusBar.setUpdatableRowtSet( this.grid.isUpdateable(), this.grid.canBeUpdateable() );
   }
   
   private void printTable()
   {      
      JTextField txtHeader  = new JTextField( this.editor.getText() );
      JTextField txtFooter  = new JTextField( "Page - {0}" );
      JCheckBox  chkPageDlg = new JCheckBox( "Show page properties dialog" );
                 chkPageDlg.setSelected( true );
      JCheckBox  chkPreview = new JCheckBox( "Show print preview" );
                 chkPreview.setSelected( true );
      
      JPanel     panel      = new JPanel( new SpringLayout() );
                 panel.add( new JLabel( "Page Header" ) );
                 panel.add( txtHeader );
                 panel.add( new JLabel( "Page Footer" ) );
                 panel.add( txtFooter );
                 panel.add( new JLabel() ); // Trick para encuadrar
                 panel.add( chkPageDlg );
                 panel.add( new JLabel() ); // Trick para encuadrar
                 panel.add( chkPreview );      
      SpringUtilities.makeCompactGrid( panel, 4,2,3,8,3,8 );
      
      JxDialog   dialog     = new JxDialog( "Table print properties" );
                 dialog.getContentPane().add( panel, BorderLayout.CENTER );
                 dialog.pack();
                 dialog.center();
                 dialog.setVisible( true );
      
      if( dialog.getCloseOption() == JxDialog.ACCEPTED )
      {
         String sHeader = txtHeader.getText();
         
         if( sHeader.length() > 77 )
            sHeader = sHeader.substring( 0,76 ) +"...";

         this.grid.printData( sHeader, txtFooter.getText(), chkPageDlg.isSelected(), chkPreview.isSelected() );
      }
   }
   
   /**
    * This method show a dialog for export the results to a plain text file.
    *
    */
   private void exportTable() {
		
		final JTextField txtPath = new JTextField(30);

			//Initilize the Jtextfield with fixedLength
		final JTextField txtSeparator = new JTextField(new jdbcmanager.swing.text.FixedLengthDocument(1), ";", 1);
		txtSeparator.setToolTipText("Separator of columns");
		JButton btnBrowse = new JButton("...");
		btnBrowse.setToolTipText("Click for Browse ...");
		final JFileChooser chooser = new JFileChooser();
		btnBrowse.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				int status = chooser.showSaveDialog(jdbcmanager.app.App.getFrameMain());
				if (status == JFileChooser.APPROVE_OPTION) txtPath.setText(chooser.getSelectedFile().getAbsolutePath());
			}
		});
		final JCheckBox jcTab = new JCheckBox("Tab");
		jcTab.setToolTipText("Define Tab char for separator");
		jcTab.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
					txtSeparator.setEnabled(!jcTab.isSelected());
			}
		});
		   	
		GridBagLayout layout = new GridBagLayout();
		JPanel panel = new JPanel(layout);
		JLabel label = new JLabel("Path:");
		GridBagConstraints c = new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, 0, new java.awt.Insets(0, 0, 8, 5), 0, 0);
		layout.setConstraints(label, c);
		panel.add(label);
		c = new GridBagConstraints(1, 0, 3, 1, 0.0, 0.0, GridBagConstraints.CENTER, 0, new java.awt.Insets(0, 0, 8, 5), 0, 0);
		layout.setConstraints(txtPath, c);
		panel.add(txtPath);
		c = new GridBagConstraints(4, 0, 2, 1, 0.0, 0.0, GridBagConstraints.CENTER, 0, new java.awt.Insets(0, 0, 8, 5), 0, 0);
		layout.setConstraints(btnBrowse, c);
		panel.add(btnBrowse);
		label = new JLabel("Separator: ");
		c = new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, 0, new java.awt.Insets(0, 0, 8, 5), 0, 0);
		layout.setConstraints(label, c);
		panel.add(label);
		c = new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, 0, new java.awt.Insets(0, 0, 8, 5), 0, 0);
		layout.setConstraints(txtSeparator, c);
		panel.add(txtSeparator);
		c = new GridBagConstraints(2, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, 0, new java.awt.Insets(0, 0, 8, 5), 0, 0);
		layout.setConstraints(jcTab, c);
		panel.add(jcTab);
			//dialogo comun.
		JxDialog   dialog     = new JxDialog( "Export Data" );
		dialog.getContentPane().add( panel, BorderLayout.CENTER );
		dialog.pack();
		dialog.center();
	    dialog.setVisible( true );
	    
	    if( dialog.getCloseOption() == JxDialog.ACCEPTED ) {
	    	if (txtPath.getText().trim().equals("")) 
	    		jdbcmanager.app.App.showMessage( "Export table", "You must set a fileName" );
	    	else if (txtSeparator.getText().equals("")) 
	    		jdbcmanager.app.App.showMessage( "Export table", "You must set a separator fields" );
	    	else {
	    		this.grid.exportDataToPlain( txtPath.getText(), jcTab.isSelected()?'\t':txtSeparator.getText().toCharArray()[0]);
	    	}
	    }
	}
   
   private void init( Connection conn )
   {
      // Create main components
      this.editor    = new Editor();
      this.history   = new History();
      this.grid      = new DataGrid( conn );
      this.statusBar = new StatusBar( this );

      JScrollPane spTable = new JScrollPane( this.grid );
                  spTable.setRowHeaderView( this.grid.getRowNumberer() );
      
      // Top: editor and history
      JPanel pnlEditor = new JPanel();
      		 pnlEditor.setLayout( new BorderLayout() );
      		 pnlEditor.add( this.editor , BorderLayout.CENTER );
      		 pnlEditor.add( this.history, BorderLayout.SOUTH  );
      		
      // Bottom panel (table)
      JPanel pnlDataTable = new JPanel( new BorderLayout() );
             pnlDataTable.add( spTable, BorderLayout.CENTER );
             pnlDataTable.add( this.statusBar, BorderLayout.SOUTH );

      // Split panel
      JSplitPane splitPane = new JSplitPane();
                 splitPane.setOrientation( JSplitPane.VERTICAL_SPLIT );
                 splitPane.setOneTouchExpandable( true );
                 splitPane.setTopComponent( pnlEditor );
                 splitPane.setBottomComponent( pnlDataTable );
                 splitPane.setDividerLocation( 125 );

      // this
      setLayout( new BorderLayout() );
      add( splitPane, BorderLayout.CENTER );
      
      this.editor.requestFocus();
      
      // Listeners ------------------------------------
      this.grid.getSelectionModel().addListSelectionListener( new ListSelectionListener()
      {
         public void valueChanged( ListSelectionEvent evt )
         {
            if( ! evt.getValueIsAdjusting() )
               statusBar.setCurrentRow( grid.getSelectedRow() + 1 );
         }
      } );
      
      // Changes action of "Print" item in popup menu (defined in JxTable)
      JMenuItem        itmPrint = this.grid.getPrintPopupMenuItem();
      ActionListener[] aActList = itmPrint.getActionListeners();
      
      for( int n = 0; n < aActList.length; n++ )
          itmPrint.removeActionListener( aActList[n] );
      
      itmPrint.addActionListener( new ActionListener()
          {
              public void actionPerformed( ActionEvent evt )  { DbTable.this.printTable(); }
          } );
      
      
      // set action of "Export" item in popup menu (defined in JxTable)
      JMenuItem        itmExport = this.grid.getExportPopupMenuItem();
      itmExport.setActionCommand("EXPORT");
      itmExport.addActionListener( this );
      
   }
   
   //-------------------------------------------------------------------------//
   // INNER CLASS: ExecAction
   //-------------------------------------------------------------------------//
   private final class ExecAction extends JAbstractAction
   {
      private ExecAction()
      {
         setName( "Execute" );
         setActionCommandKey( "EXECUTE" );
         setShortDescription( "Execute  [F5]" );
         setLongDescription( "Executes command shown in SQL editor" );
         setSmallIcon( new ImageIcon( getClass().getResource( "images/tb_exec.gif" ) ) );
         setMnemonicKey( 'x' );
         setKeyStroke( KeyStroke.getKeyStroke( KeyEvent.VK_F5, 0 ) );
      }
            
      public void actionPerformed( ActionEvent evt )
      {
         if( isEnabled() )
            execute();
      }
   }
   
   //-------------------------------------------------------------------------//
   // INNER CLASS: Editor
   //-------------------------------------------------------------------------//
   private final class Editor extends JSQLCodeEditor
   {
      private JxSpinner spnMaxRows;  // Maximum rows spinner
      
      private Editor()
      {
         int nMaxRows = App.props().getSQLGridDefaultMaxRows( 250 ); 
      
         this.spnMaxRows = new JxSpinner( new SpinnerNumberModel( nMaxRows, 0, Integer.MAX_VALUE, 25 ) );
         this.spnMaxRows.setToolTipText( "Maximum amount of records to be displayed (0 = no limit)" );
         this.spnMaxRows.setMaximumSize( new Dimension( 48, (int) this.spnMaxRows.getPreferredSize().getHeight() ) );
         
         // Initialises the SQL Editor
         add2ToolBar( execAction );
         add2ToolBar( new JLabel( "    Rows " ) );
         add2ToolBar( this.spnMaxRows );
         
         // Add actions to editor popup
         add2Popup( null );        // Adds a separator
         add2Popup( execAction );
      }
      
      private int getMaxRows()
      {
         return ((Integer) this.spnMaxRows.getValue()).intValue();         
      }
   }
   
   //-------------------------------------------------------------------------//
   // INNER CLASS: Editor
   //-------------------------------------------------------------------------//
   private final class History extends JPanel
   {
   		private JComboBox cmb;
   		
   		private History()
   		{
   			this.cmb = new JComboBox();
   			this.cmb.addActionListener( new ActionListener()
   					{
						public void actionPerformed( ActionEvent evt )
						{
							DbTable.this.editor.setText( (String) cmb.getSelectedItem() );
						}
   					} );
   			
   			setBorder( BorderFactory.createEmptyBorder( 5,5,5,5 ) );
   			setLayout( new BorderLayout( 4,0 ) );
   			add( new JLabel( "History" ), BorderLayout.WEST );
   			add( this.cmb, BorderLayout.CENTER );
   		}
   		
   		private void addSQL( String s )
   		{
   			ComboBoxModel cbm = this.cmb.getModel(); 
   			int nItems = cbm.getSize();
   			int n;
   			
   			for( n = 0; n < nItems; n++ )
   				if( ((String) cbm.getElementAt( n )).equals( s ) )
   					break;
   				
   			if( n >= nItems )
   				this.cmb.addItem( s );
   		}
   }
}